/*
 * OpenFileEntry.cpp
 *
 *  Created on: Jul 10, 2009
 *      Author: oferbart
 */

#include "OpenFileEntry.h"

OpenFileEntry::OpenFileEntry(FileSys* fs, int flag, int i_nodeNum, int blockSize) {
	_fs = fs;
	_position = 0;
	_refCount = 0;
	_flag = flag;
	_i_nodeNum = i_nodeNum;
	_blockSize = blockSize;
	pthread_mutex_init(&_lock, NULL);
	pthread_mutex_init(&_readLock, NULL);
	pthread_mutex_init(&_writeLock, NULL);
}

OpenFileEntry::OpenFileEntry(OpenFileEntry* oOpenFileEntry){
	_fs = oOpenFileEntry->_fs;
	_position = oOpenFileEntry->_position;
	_refCount =  oOpenFileEntry->_refCount;
	_flag =  oOpenFileEntry->_flag;
	_i_nodeNum =  oOpenFileEntry->_i_nodeNum;
	_blockSize = oOpenFileEntry->_blockSize;
	pthread_mutex_init(&_lock, NULL);
	pthread_mutex_init(&_readLock, NULL);
	pthread_mutex_init(&_writeLock, NULL);
}

OpenFileEntry::~OpenFileEntry() {
	pthread_mutex_destroy(&_lock);
	pthread_mutex_destroy(&_readLock);
	pthread_mutex_destroy(&_writeLock);
}

int OpenFileEntry::setPosition(int position){
	pthread_mutex_lock(&_lock);

	int maxOffset = ((_blockSize / 4) + 10) * _blockSize;
	if (position < 0 || position >= maxOffset){
		ostringstream str;
		str << "OpenFileEntry::setPosition - set Position is out of bound [0," << maxOffset-1 << "]: ";
		str<< position << endl;
		Log::log(str, 0, "error");
		return -1;
	}

	this->_position = position;

	pthread_mutex_unlock(&_lock);
	return position;
}

int OpenFileEntry::getPosition(){
	pthread_mutex_lock(&_lock);

	int res = this->_position;

	pthread_mutex_unlock(&_lock);
	return res;
}

int OpenFileEntry::relPosition(int offset){
	pthread_mutex_lock(&_lock);

	int newPos = this->_position + offset;
	int maxOffset = ((_blockSize / 4) + 10) * _blockSize;
	if (newPos < 0 || newPos >= maxOffset){
		ostringstream str;
		str << "OpenFileEntry::maxOffset - set Position is out of bound [0," << maxOffset-1 << "]: ";
		str<< newPos << endl;
		Log::log(str, 0, "error");
		return -1;
	}

	this->_position = newPos;

	pthread_mutex_unlock(&_lock);
	return newPos;
}


int OpenFileEntry::incRef(){
	pthread_mutex_lock(&_lock);

	_refCount++;

	pthread_mutex_unlock(&_lock);
	return _refCount;
}

int OpenFileEntry::decRef(){
	pthread_mutex_lock(&_lock);
	_refCount--;
	if (_refCount < 0){
		ostringstream str; str << "OpenFileEntry::decRef - refCount is negative = " << _refCount << endl;
		Log::log(str, 0, "error");
	}

	pthread_mutex_unlock(&_lock);
	return _refCount;
}

// reads nBytes from the received file into the buffer.
/// returns number of bytes read.
int OpenFileEntry::Read (int nBytes, unsigned char* buffer){
	pthread_mutex_lock(&_lock);
	int result = 0;

	if (!(_flag == READ | _flag == READWRITE)){
		ostringstream str; str << "OpenFileEntry::Read - no permission to read. permission = " << _flag << endl;
		Log::log(str, 0, "error");
		return -1;
	}

	if (_isLockedWrite){
		if (_writeLockingThread != pthread_self()){
			ostringstream str; str << "OpenFileEntry::Read - read is locked on write by another thread" << endl;
			Log::log(str, 0, "error");
			return -1;
		}
	}

	result = _fs->f_read(_i_nodeNum, buffer, _position, nBytes);

	pthread_mutex_unlock(&_lock);

	return result;
}

// writes nBytes from buffer into the file.
/// returns number of bytes written.
int OpenFileEntry::Write (int nBytes,unsigned char* buffer){
	pthread_mutex_lock(&_lock);
	int result = 0;

	if (!(_flag == WRITE | _flag == READWRITE)){
		ostringstream str; str << "OpenFileEntry::Write - no permission to write. permission = " << _flag << endl;
		Log::log(str, 0, "error");
		return -1;
	}

	if (_isLockedRead){
		ostringstream str; str << "OpenFileEntry::Write - file is locked on read" << endl;
		Log::log(str, 0, "error");
		return -1;
	}

	result = _fs->f_write(_i_nodeNum, buffer, _position, nBytes);

	pthread_mutex_unlock(&_lock);

	return result;
}

// locks file, so that only read operations are allowed.
// There is no limit on the number of processes that are allowed to concurrently read the file.
// No processes are allowed to write to the file if there is an open read lock on the file.
// It is not obligatory to lock the file to read it.
// You should not block if it is locked rather return an error.
int OpenFileEntry::lockRead(){ // only multi read, no write
	pthread_mutex_lock(&_lock);

	int result = 0;

	if (_isLockedWrite){
		ostringstream str; str << "OpenFileEntry::lockRead - cannot lock on read because already locked on write." << endl;
		Log::log(str, 0, "error");
		result = -1;
	} else {
		_isLockedRead = true;
		_readLockingThread = pthread_self();
	}

	pthread_mutex_unlock(&_lock);

	return result;
}

// locks file, so that only one process may hold the lock.
// This process may write to the file, and no other files may write to it or read from it.
// A child does not inherit this lock. It is not obligatory to lock the file to write to it.
// You should not block if it is locked rather return an error
int OpenFileEntry::lockWrite(){ // only one writer
	pthread_mutex_lock(&_lock);
	int result = 0;

	if (_isLockedRead){
		ostringstream str; str << "OpenFileEntry::lockWrite - cannot lock on write because already locked on read." << endl;
		Log::log(str, 0, "error");
		result = -1;
	}
	else {
		_isLockedWrite = true;
		_writeLockingThread = pthread_self();
	}

	pthread_mutex_unlock(&_lock);

	return result;
}

// releases the reading lock held by this process.
// Only the process which locked the file may release the lock.
int OpenFileEntry::releaseLockRead(){
	releaseLockRead(false);
}

int OpenFileEntry::tryReleaseLockRead(){
	releaseLockRead(true);
}

int OpenFileEntry::releaseLockRead(bool supressErrors){
	pthread_mutex_lock(&_lock);

	int result = 0;

	if (_readLockingThread == pthread_self()){
		_isLockedRead = false;
		_readLockingThread = -9;
	} else {
		if (!supressErrors){
			ostringstream str; str << "OpenFileEntry::releaseLockRead - cannot release read lock because it was locked by someone else." << endl;
			Log::log(str, 0, "error");
		}
		result = -1;
	}

	pthread_mutex_unlock(&_lock);

	return result;
}

// releases the writing lock held by this process.
// Only the process that locked the file may release this lock.
int OpenFileEntry::releaseLockWrite(){
	releaseLockWrite(false);
}

int OpenFileEntry::tryReleaseLockWrite(){
	releaseLockWrite(true);
}

int OpenFileEntry::releaseLockWrite(bool supressErrors){
	pthread_mutex_lock(&_lock);

	int result = 0;

	if (_writeLockingThread == pthread_self()){
		_isLockedWrite = false;
		_writeLockingThread = -9;
	} else {
		if (!supressErrors){
			ostringstream str; str << "OpenFileEntry::releaseLockWrite - cannot release write lock because it was locked by someone else." << endl;
			Log::log(str, 0, "error");
			result = -1;
		}
	}

	pthread_mutex_unlock(&_lock);

	return result;
}

int OpenFileEntry::getInodeNum(){
	pthread_mutex_lock(&_lock);

	int result = _i_nodeNum;

	pthread_mutex_unlock(&_lock);

	return result;

}

int OpenFileEntry::getPermission(){
	return _flag;
}

/*bool OpenFileEntry::isMutexLocked(int lockType){

	bool result = true;

	if (lockType == READLOCK){
		result = _readLockingThread != 0;
	}

	if (lockType == READLOCK){
		result = _readLockingThread != 0;
	}

	return result;
}*/

bool OpenFileEntry::canRead(){
	bool result = !_isLockedWrite;

	return result;
}

bool OpenFileEntry::canWrite(){

	if (_isLockedRead){
		return false;
	}

	if (_isLockedWrite){
		// if we are holding the lock we can write
		pthread_t self = pthread_self();
		if (self == _readLockingThread){
			return true;
		} else {
			return false;
		}
	} else {
		return true;
	}
}







